﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System;
using CdgNetPersistencia.ClasesBases;
using CdgNetPersistencia.Componentes;
using System.Reflection;
using CdgNetPersistencia.Atributos;

/**
    * Autor :  Cristian Daniel Garay Sosa
    * Fecha :  26/03/2012 (v2)
    * Comentarios:
    *          gestiona la interaccion de la aplicacion con los datos de una tabla y sus relaciones
    * 
    * Cambios:
    *          26/03/2012 - CDGS - Agregado Set_registros()
    *                              Transfiere los datos de un registro de tabla a su OTD correspondiente
    * 
    * **/

namespace CdgNetPersistencia.ClasesBases
{
    /// <summary>
    /// Clase base de estructura de un administrador de Tabla
    /// </summary>
    public abstract class ADMbase
    {

        #region CAMPOS(Fields) DE LA CLASE

        public string NOMBRE_CLASE = "ADMbase";

        /// <summary>
        /// Instancia relacionada de la clase TBLbase
        /// </summary>
        protected TBLbase _oTabla;

        /// <summary>
        /// Instancia de la clase de utileria de conexion a la base de datos
        /// </summary>
        protected ConectorBase _oConector;

        /// <summary>
        /// Coleccion de instancias de registros
        /// </summary>
        protected List<OTDbase> _lInstancias;

        // cadenas de sentencias bases
        private const string __INSERT_SQL = "INSERT INTO {0} ({1}) VALUES ({2})";
        private const string __UPDATE_SQL = "UPDATE {0} SET {1} ";
        private const string __DELETE_SQL = "DELETE FROM {0} ";

        #endregion



        #region CONSTRUCTORES

        /// <summary>
        /// Contructor por defecto
        /// </summary>
        public ADMbase()
        {}

        /// <summary>
        /// Contructor de la clase
        /// </summary>
        /// <param name="oConectorBase">Instancia de utileria de interaccion con la base de datos</param>
        public ADMbase(ConectorBase oConectorBase)
        {
            _oConector = oConectorBase;

        }


        #endregion



        #region GETTERS Y SETTERS

        /// <summary>
        /// Instancia relacionada de la clase TBLbase
        /// </summary>
        protected void Set_tabla(TBLbase oTablaParam)
        {
            //asignamos la instancia de estructura de tabla a manejar
            _oTabla = oTablaParam;

            //establecemos los comandos DML
            _Select_sql = _oTabla.Get_select_basico;
            _Insert_sql = _oTabla.Get_insert_basico;
            _Update_sql = _oTabla.Get_update_basico;
            _Delete_sql = _oTabla.Get_delete_basico;

        }
        

        /// <summary>
        /// Devuelve o establece el comando de seleccion de datos de la tabla
        /// </summary>
        protected string _Select_sql { get; set; }

        /// <summary>
        /// Devuelve o establece el comando de insercion de datos a la tabla
        /// </summary>
        protected string _Insert_sql { get; set; }

        /// <summary>
        /// Devuelve o establece el comando de actualizacion de datos de la tabla
        /// </summary>
        protected string _Update_sql { get; set; }

        /// <summary>
        /// Devuelve o establece el comando de aliminacion de datos de la tabla
        /// </summary>
        protected string _Delete_sql { get; set; }

        /// <summary>
        /// Devuelve o establece el diccionario de relacion de 
        /// Propiedad (get, set) de OTD y campo de TBL
        /// </summary>
        protected Dictionary<PropertyInfo, CampoTabla> _dicATR_TBL { get; set; }


        #endregion



        #region METODOS DML

        /// <summary>
        /// Inserta un nuevo registro en la tabla a partir de la instancia
        /// </summary>
        /// <param name="oOTDbase">Instancia de la clase que representa a un registro</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lAgregar(OTDbase oOTDbase);

        /// <summary>
        /// Actualiza el registro en la tabla a partir de la instancia
        /// </summary>
        /// <param name="oOTDbase">Instancia de la clase que representa a un registro</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lActualizar(OTDbase oOTDbase);

        /// <summary>
        /// Elimina el registro de la tabla representado por la instancia
        /// </summary>
        /// <param name="oOTDbase">Instancia de la clase que representa a un registro</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lEliminar(OTDbase oOTDbase);

        /// <summary>
        /// Obtiene una unica instancia que coincide con el Id buscado
        /// </summary>
        /// <param name="oOTDbase">Instancia de la clase que representa a un registro</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lGet_elemento(OTDbase oOTDbase);

        /// <summary>
        /// Obtiene una lista de instancias que satisfacen 
        /// la condicion de filtrado desde la base de datos
        /// </summary>
        /// <param name="cFiltroWhere">Condicion de filtrado de registros</param>
        /// <param name="dicParametros">Diccionario de claves y valores de parametros de la sentencia</param>
        /// <returns>Lista de resultado [entero, object]</returns>
        public abstract List<object> lGet_elementos(string cFiltroWhere, Dictionary<string, object> dicParametros);


        #endregion


        #region METODOS DE PASO OTD -> DATOS - DATOS -> OTD


        /// <summary>
        /// Establece el diccionario de relacionamiento Propiedad (get, set) de OTD - CAMPO de TBL
        /// </summary>
        /// <typeparam name="T">Instancia de OTDbase extendida</typeparam>
        /// <param name="lInstancias">Lista de instancias para tomar la clase resultante</param>
        protected void Set_campos_atributos<T>(List<T> lInstancias)
        {
            //si aun no esta instanciado
            if (_dicATR_TBL == null)
            {
                //instanciamos un nuevo diccionario
                _dicATR_TBL = new Dictionary<PropertyInfo, CampoTabla>();

                try
                {
                    //tomamos el tipo de la instancia
                    Type tTipoOtTD = typeof(T);

                    //tomamos los campos de la clase
                    PropertyInfo[] aPropiedades = tTipoOtTD.GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);

                    //creamos una instancia del OTD extendido
                    object oInstanciaOTD = Activator.CreateInstance<T>();

                    //recorremos los metodos de la clase actual
                    foreach (PropertyInfo propiedad in aPropiedades)
                    {
                        //http://msdn.microsoft.com/es-es/library/z919e8tw%28v=vs.80%29.aspx
                        //recorremos sus atributos
                        foreach (object atributo in propiedad.GetCustomAttributes(true))
                        {
                            //si es del tipo Campo
                            if (atributo is Campo)
                            {
                                //lo tomamos 
                                Campo oCampo = (Campo)atributo;

                                //si el campo esta en la estructura actual
                                if (_oTabla.CAMPOS.ContainsKey(oCampo.GetNombre))
                                {
                                    //tomamos la instancia correspondiente y la agregamos al diccionario
                                    _dicATR_TBL.Add(propiedad, _oTabla.CAMPOS[oCampo.GetNombre]);
                                }
                                else
                                {
                                    //sino, error
                                    Exception ex = new Exception(string.Format("No se encuentra la referencia al campo {0} en la estructura de la tabla {1}"
                                                                        , oCampo.GetNombre
                                                                        , _oTabla.Get_nombre_tabla
                                                                        )
                                                            );
                                    throw ex;

                                }
                            }
                        }
                    }

                }
                catch (Exception ex) { throw ex; }
            }

        }
        
        /// <summary>
        /// Devuelve el comando de insercion en tabla para la instancia actual
        /// </summary>
        /// <param name="OTDparam">Instancia de clase extendida de OTDbase</param>
        /// <returns>Ccadena de Comando Insert SQL</returns>
        public List<object> lGet_insert<T>(ref T OTDparam)
        {

            string NOMBRE_METODO = NOMBRE_CLASE + ".lGet_insert()";

            //resultado por defecto
            List<object> lResultado = new List<object>() { 0, NOMBRE_METODO + " No Ejecutado!."};

            try
            {
                //establecemos el diccionario de relacion propiedad - campo
                Set_campos_atributos<T>(new List<T>() { OTDparam });

                //tomamos el tipo de la instancia
                Type tTipoOtTD = typeof(T);

                //sentencia inicial
                string cInsertSQL = _oTabla.Get_insert_aliases;

                //recorremos los metodos del diccionario
                foreach (PropertyInfo propiedad in _dicATR_TBL.Keys)
                {
                    //reemplazamos el alias del campo por el valor de atributo de la instancia
                    cInsertSQL = cInsertSQL.Replace(_dicATR_TBL[propiedad].Get_alias
                                                    , tTipoOtTD.GetProperty(propiedad.Name).GetValue(OTDparam, null).ToString()
                                                    );

                }
                
                //establecemos el metodo del resultado
                lResultado = new List<object>() { 1, cInsertSQL };

            }
            catch (Exception ex)
            {
                //en caso de error
                lResultado = new List<object>() { -1, NOMBRE_METODO + " Error: " + ex.Message };

            }

            //devolvemos el resultado
            return lResultado;

        }
        
        /// <summary>
        /// Ejecuta la instanciacion y asignacion de los OTDs 
        /// </summary>
        /// <typeparam name="T">Clase de OTDbase extendida a los que seran 'convertidas' las filas</typeparam>
        // <param name="lInstancias">Lista de OTDs a cargar con las instancias generadas</param>
        /// <param name="aFilasParam">Arreglo de filas de un DataTable</param>
        /// <see cref="http://www.csharp-examples.net/reflection-examples/"/>
        //public List<T> lSet_registros<T>(out List<T> lInstancias, DataRowCollection aFilasParam)
        public List<T> lSet_registros<T>(DataRowCollection aFilasParam)
        {
            //reinstanciamos la lista a cargar
            List<T> lInstancias = new List<T>() { };

            //tomamos el tipo de la instancia
            Type tTipoOtTD = typeof(T);

            //establecemos el diccionario de relacion propiedad - campo
            Set_campos_atributos<T>(lInstancias);

            //recorremos las filas de tabla
            foreach (DataRow dr in aFilasParam)
            {
                //creamos una instancia del OTD extendido
                object oInstanciaOTD = Activator.CreateInstance<T>();
                
                //recorremos los metodos del diccionario
                foreach (PropertyInfo propiedad in _dicATR_TBL.Keys)
                {
                    //si no es un valor nulo
                    if (dr[_dicATR_TBL[propiedad].cNombre].GetType() != typeof(DBNull))
                        //llamamos al metodo setter actual
                        tTipoOtTD.InvokeMember(propiedad.Name
                                                , BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public
                                                , null, oInstanciaOTD, new object[] { dr[_dicATR_TBL[propiedad].cNombre] }
                                                );
                }

                //agregamos la instancia nueva a la coleccion
                lInstancias.Add((T)oInstanciaOTD);

            }

            //devolvemos el resultado del metodo
            return lInstancias;

        }


        #endregion


        #region SOBREESCRITOS

        /// <summary>
        /// Devuelve la representacion de la clase
        /// </summary>
        /// <returns>Representacion de cadena de la clase</returns>
        public override string ToString()
        {
            return string.Format("<{0}: {1}> ", NOMBRE_CLASE, _oTabla.Get_nombre_tabla);
        }


        #endregion


    }
}
